#include "preHeader.h"
#include "Session/GameSession.h"
#include "Session/GameSessionHandler.h"
#include "SQLHelper.h"

#define SQL_INSERT_MAIL_LENGTH_LIMIT (std::ios::pos_type(1 << 19))

int GameSessionHandler::HandleAcquireMailExpireTime(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto AcquireMailExpireTime = [GSRPCAsyncTaskArgs]() {
		NetBuffer buffer;
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (connPtr) {
			auto rst = connPtr->QueryFormat(
				"SELECT MIN(mailExpireTime) FROM `%s`", GetTableName<inst_mail>());
			if (rst) {
				if (rst.NextRow()) {
					buffer << rst.Fetch().GetInt64();
				}
				GameSession::Feedback(gsPtr, buffer, info.sn);
			} else {
				GameSession::FeedbackError(gsPtr, info.sn, DBPErrorQueryMysqlFailed);
			}
		} else {
			GameSession::FeedbackError(gsPtr, info.sn, DBPErrorConnectMysqlFailed);
		}
	};
	sDeferAsyncTaskMgr.AddTask(CreateAsyncTask(AcquireMailExpireTime));
	return SessionHandleCapture;
}

int GameSessionHandler::HandleCleanExpireMail(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto CleanExpireMail = [GSRPCAsyncTaskArgs]() {
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (connPtr) {
			auto rst = connPtr->ExecuteFormat(
				"DELETE FROM `%s` WHERE mailExpireTime<%lld",
				GetTableName<inst_mail>(), s64(GET_UNIX_TIME));
			if (rst.second) {
				GameSession::Feedback(gsPtr, ConstNetBuffer(), info.sn);
			} else {
				GameSession::FeedbackError(gsPtr, info.sn, DBPErrorDeleteMysqlFailed);
			}
		} else {
			GameSession::FeedbackError(gsPtr, info.sn, DBPErrorConnectMysqlFailed);
		}
	};
	sDeferAsyncTaskMgr.AddTask(CreateAsyncTask(CleanExpireMail));
	return SessionHandleCapture;
}

int GameSessionHandler::HandleGetPlayerMailCount(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto GetPlayerMailCount = [GSRPCAsyncTaskArgs]() {
		NetBuffer buffer;
		auto playerId = pckPtr->Read<uint32>();
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (connPtr) {
			auto rst = connPtr->QueryFormat(
				"SELECT COUNT(*) FROM `%s` WHERE mailReceiver=%u",
				GetTableName<inst_mail>(), playerId);
			if (rst) {
				rst.NextRow();
				buffer << rst.Fetch().GetUInt32();
				GameSession::Feedback(gsPtr, buffer, info.sn);
			} else {
				GameSession::FeedbackError(gsPtr, info.sn, DBPErrorDeleteMysqlFailed);
			}
		} else {
			GameSession::FeedbackError(gsPtr, info.sn, DBPErrorConnectMysqlFailed);
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(GetPlayerMailCount));
	return SessionHandleCapture;
}

int GameSessionHandler::HandleGetPlayerMailList(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto GetPlayerMailList = [GSRPCAsyncTaskArgs]() {
		TNetBuffer<65536> buffer;
		auto playerId = pckPtr->Read<uint32>();
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (connPtr) {
			auto sqlFormat = CreateSQL4SelectEntity<inst_mail>(
				"mailReceiver=%u ORDER BY mailDeliverTime LIMIT 20");
			auto rst = connPtr->QueryFormat(sqlFormat.c_str(), playerId);
			if (rst) {
				while (rst.NextRow()) {
					auto row = rst.Fetch();
					auto instInfo = LoadEntityFromMysqlRow<inst_mail>(row);
					SaveToINetStream(instInfo, buffer);
				}
				GameSession::Feedback(gsPtr, buffer, info.sn);
			} else {
				GameSession::FeedbackError(gsPtr, info.sn, DBPErrorDeleteMysqlFailed);
			}
		} else {
			GameSession::FeedbackError(gsPtr, info.sn, DBPErrorConnectMysqlFailed);
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(GetPlayerMailList));
	return SessionHandleCapture;
}

int GameSessionHandler::HandleGetPlayerMailAttachment(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto GetPlayerMailAttachment = [GSRPCAsyncTaskArgs]() {
		TNetBuffer<65536> buffer;
		auto playerId = pckPtr->Read<uint32>();
		auto idList = LoadSQLXListFromINetStream<uint32>(*pckPtr);
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (connPtr) {
			auto sqlFormat = CreateSQL4SelectEntity<InstMailAttachment>(
				"mailReceiver=%u AND !isGetAttachment AND mailID IN (%s)");
			auto rst = connPtr->QueryFormat(
				sqlFormat.c_str(), playerId, idList.c_str());
			if (rst) {
				while (rst.NextRow()) {
					auto row = rst.Fetch();
					auto instInfo = LoadEntityFromMysqlRow<InstMailAttachment>(row);
					SaveToINetStream(instInfo, buffer);
				}
				GameSession::Feedback(gsPtr, buffer, info.sn);
			} else {
				GameSession::FeedbackError(gsPtr, info.sn, DBPErrorDeleteMysqlFailed);
			}
		} else {
			GameSession::FeedbackError(gsPtr, info.sn, DBPErrorConnectMysqlFailed);
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(GetPlayerMailAttachment));
	return SessionHandleCapture;
}

int GameSessionHandler::HandlePlayerGetMailAttachment(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto PlayerGetMailAttachment = [GSRPCAsyncTaskArgs]() {
		NetBuffer buffer;
		auto playerId = pckPtr->Read<uint32>();
		auto idList = LoadSQLXListFromINetStream<uint32>(*pckPtr);
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (connPtr) {
			auto sqlFormat = "UPDATE `%s` SET isGetAttachment=1 "
				"WHERE mailReceiver=%u AND mailID IN (%s)";
			auto rst = connPtr->ExecuteFormat(sqlFormat,
				GetTableName<inst_mail>(), playerId, idList.c_str());
			if (rst.second) {
				buffer << (u32)rst.first;
				GameSession::Feedback(gsPtr, buffer, info.sn);
			} else {
				GameSession::FeedbackError(gsPtr, info.sn, DBPErrorDeleteMysqlFailed);
			}
		} else {
			GameSession::FeedbackError(gsPtr, info.sn, DBPErrorConnectMysqlFailed);
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(PlayerGetMailAttachment));
	return SessionHandleCapture;
}

int GameSessionHandler::HandlePlayerViewMailDetail(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto PlayerViewMailDetail = [GSRPCAsyncTaskArgs]() {
		NetBuffer buffer;
		auto playerId = pckPtr->Read<uint32>();
		auto idList = LoadSQLXListFromINetStream<uint32>(*pckPtr);
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (connPtr) {
			auto sqlFormat = "UPDATE `%s` SET isViewDetail=1 "
				"WHERE mailReceiver=%u AND mailID IN (%s)";
			auto rst = connPtr->ExecuteFormat(sqlFormat,
				GetTableName<inst_mail>(), playerId, idList.c_str());
			if (rst.second) {
				buffer << (u32)rst.first;
				GameSession::Feedback(gsPtr, buffer, info.sn);
			} else {
				GameSession::FeedbackError(gsPtr, info.sn, DBPErrorDeleteMysqlFailed);
			}
		} else {
			GameSession::FeedbackError(gsPtr, info.sn, DBPErrorConnectMysqlFailed);
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(PlayerViewMailDetail));
	return SessionHandleCapture;
}

int GameSessionHandler::HandlePlayerWriteMail(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto PlayerWriteMail = [GSRPCAsyncTaskArgs]() {
		NetBuffer buffer;
		inst_mail instInfo;
		LoadFromINetStream(instInfo, *pckPtr);
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (connPtr) {
			auto sqlStr = CreateSQL4InsertEntity(instInfo);
			auto rst = connPtr->Execute(sqlStr.c_str());
			if (rst.second) {
				buffer << instInfo.mailReceiver;
				GameSession::Feedback(gsPtr, buffer, info.sn);
			} else {
				GameSession::FeedbackError(gsPtr, info.sn, DBPErrorInsertMysqlFailed);
			}
		} else {
			GameSession::FeedbackError(gsPtr, info.sn, DBPErrorConnectMysqlFailed);
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(PlayerWriteMail));
	return SessionHandleCapture;
}

int GameSessionHandler::HandlePlayerDeleteMail(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto PlayerDeleteMail = [GSRPCAsyncTaskArgs]() {
		NetBuffer buffer;
		auto playerId = pckPtr->Read<uint32>();
		auto idList = LoadSQLXListFromINetStream<uint32>(*pckPtr);
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (connPtr) {
			auto sqlFormat = "DELETE FROM `%s` "
				"WHERE mailReceiver=%u AND mailID IN (%s)";
			auto rst = connPtr->ExecuteFormat(sqlFormat,
				GetTableName<inst_mail>(), playerId, idList.c_str());
			if (rst.second) {
				buffer << (u32)rst.first;
				GameSession::Feedback(gsPtr, buffer, info.sn);
			} else {
				GameSession::FeedbackError(gsPtr, info.sn, DBPErrorDeleteMysqlFailed);
			}
		} else {
			GameSession::FeedbackError(gsPtr, info.sn, DBPErrorConnectMysqlFailed);
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(PlayerDeleteMail));
	return SessionHandleCapture;
}

int GameSessionHandler::HandleSystemMultiSameMail(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto SystemMultiSameMail = [GSRPCAsyncTaskArgs]() {
		auto mailCount = pckPtr->Read<uint32>();
		std::vector<uint32> receiverUids;
		receiverUids.reserve(mailCount);
		for (uint32 i = 0; i < mailCount; ++i) {
			receiverUids.push_back(pckPtr->Read<uint32>());
		}
		inst_mail instInfo;
		LoadFromINetStream(instInfo, *pckPtr);
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (connPtr) {
			TNetBuffer<65536> buffer;
			std::ostringstream sqlStream;
			for (uint32 i = 0; i < mailCount; ++i) {
				instInfo.mailReceiver = receiverUids[i];
				buffer << instInfo.mailReceiver;
				if (sqlStream.tellp() == std::ios::pos_type(0)) {
					sqlStream << CreateSQL4InsertEntity(instInfo);
				} else {
					sqlStream << CreateSQL4InsertEntityValues(instInfo);
				}
				if (sqlStream.tellp() > SQL_INSERT_MAIL_LENGTH_LIMIT || i + 1 >= mailCount) {
					if (connPtr->Execute(sqlStream.str().c_str()).second) {
						GameSession::Feedback(gsPtr, buffer, info.sn, RPCErrorNone, false);
					}
					buffer.Clear(), sqlStream.str("");
				}
			}
			GameSession::Feedback(gsPtr, buffer, info.sn, DBPErrorNone);
		} else {
			GameSession::FeedbackError(gsPtr, info.sn, DBPErrorConnectMysqlFailed);
		}
	};
	sDeferAsyncTaskMgr.AddTask(CreateAsyncTask(SystemMultiSameMail));
	return SessionHandleCapture;
}

int GameSessionHandler::HandleSystemMultiDifferentMail(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto SystemMultiDifferentMail = [GSRPCAsyncTaskArgs]() {
		auto mailCount = pckPtr->Read<uint32>();
		std::vector<inst_mail> instInfos;
		instInfos.reserve(mailCount);
		for (uint32 i = 0; i < mailCount; ++i) {
			instInfos.push_back({});
			LoadFromINetStream(instInfos.back(), *pckPtr);
		}
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (connPtr) {
			TNetBuffer<65536> buffer;
			std::ostringstream sqlStream;
			for (uint32 i = 0; i < mailCount; ++i) {
				buffer << instInfos[i].mailReceiver;
				if (sqlStream.tellp() == std::ios::pos_type(0)) {
					sqlStream << CreateSQL4InsertEntity(instInfos[i]);
				} else {
					sqlStream << CreateSQL4InsertEntityValues(instInfos[i]);
				}
				if (sqlStream.tellp() > SQL_INSERT_MAIL_LENGTH_LIMIT || i + 1 >= mailCount) {
					if (connPtr->Execute(sqlStream.str().c_str()).second) {
						GameSession::Feedback(gsPtr, buffer, info.sn, RPCErrorNone, false);
					}
					buffer.Clear(), sqlStream.str("");
				}
			}
			GameSession::Feedback(gsPtr, buffer, info.sn, DBPErrorNone);
		} else {
			GameSession::FeedbackError(gsPtr, info.sn, DBPErrorConnectMysqlFailed);
		}
	};
	sDeferAsyncTaskMgr.AddTask(CreateAsyncTask(SystemMultiDifferentMail));
	return SessionHandleCapture;
}
